package com.pkk.penguin.frame.know.boot.config.handle.zhiyi.db.support.support;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jcraft.jsch.JSchException;
import com.pkk.penguin.frame.know.boot.config.common.handle.AbstractWebSocketServer;
import com.pkk.penguin.frame.know.boot.config.common.handle.AbstractWebSocketServer.WebScoketUtil;
import com.pkk.penguin.frame.know.boot.config.handle.zhiyi.db.support.util.ExecUtil;
import com.pkk.penguin.support.ssh.common.SpringContextUtils;
import com.pkk.penguin.support.ssh.exception.SSHActiveException;
import com.pkk.penguin.support.ssh.support.SSHConnectionSupport;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;

/**
 * @description: 平台执行支持
 * @author: peikunkun
 * @create: 2020-03-02 09:55
 **/
@Slf4j
public class ExecPingTaiSingleSupport {

  /**
   * 执行是否启用线程
   */
  private static SSHConnectionSupport sshConnectionSupport;
  private static ExecPingTaiSingleSupport execPingTaiSingleSupport = null;

  /**
   * 核心线程数
   */
  private static Integer DEFAULT_CORE_THREAD_NUM = 15;
  /**
   * 线程器
   */
  private static ExecutorService executorService = null;
  /**
   * 默认的核心线程数
   */
  private static int coreThread = 1;


  static {
    final int pingtaiSize = ExecUtil.PINGTAI_KEY_INFO.size();
    coreThread = pingtaiSize <= DEFAULT_CORE_THREAD_NUM ? pingtaiSize : DEFAULT_CORE_THREAD_NUM;
    executorService = Executors.newFixedThreadPool(coreThread);
    log.info("[核心线程数创建]-当前操作平台数[{}]-创建核心数[{}]", pingtaiSize, coreThread);
  }

  private ExecPingTaiSingleSupport() {
  }

  static {
    if (null == sshConnectionSupport) {
      sshConnectionSupport = (SSHConnectionSupport) SpringContextUtils.getBean("SSHConnectionSupport");
    }
    if (null == sshConnectionSupport) {
      throw new NoSuchBeanDefinitionException(SSHConnectionSupport.class);
    }
  }

  /**
   * @Description: 获取执行实例
   * @return: com.pkk.penguin.exec.pingtai.support.ExecPingTaiSingleSupport
   * @Author: peikunkun
   * @Date: 2020/3/2 0002 上午 9:58
   */
  public static ExecPingTaiSingleSupport getInstance() {
    if (null == execPingTaiSingleSupport) {
      synchronized (ExecPingTaiSingleSupport.class) {
        if (null == execPingTaiSingleSupport) {
          execPingTaiSingleSupport = new ExecPingTaiSingleSupport();
        }
      }
    }
    return execPingTaiSingleSupport;
  }

  /**
   * @Description: 执行器
   * @Param: execPingTai
   * @Param execSQL
   * @Author: peikunkun
   * @Date: 2020/3/2 0002 上午 10:02
   */
  public JSONArray exec(Set<String> execPingTai, String execSQL) {
    return exec(execPingTai, execSQL, false);
  }


  /**
   * @Description: 高速执行
   * @Param: execPingTai
   * @Param execSQL
   * @return: com.alibaba.fastjson.JSONObject
   * @Author: peikunkun
   * @Date: 2020/3/4 0004 下午 3:31
   */
  public JSONArray execPlus(Set<String> execPingTai, String execSQL) {
    return execThread(execPingTai, execSQL, false, null);
  }

  /**
   * @Description: 高速执行
   * @Param: execPingTai
   * @Param execSQL
   * @return: com.alibaba.fastjson.JSONObject
   * @Author: peikunkun
   * @Date: 2020/3/4 0004 下午 3:31
   */
  public JSONArray execPlus(Set<String> execPingTai, String execSQL, boolean forceExec,
      AbstractWebSocketServer webSocketServer) {
    return execThread(execPingTai, execSQL, forceExec, webSocketServer);
  }


  /**
   * @Description: 执行器
   * @Param: execPingTai
   * @Param execSQL
   * @Param: forceExec: 是否迫切执行
   * @Param: enableThread: 是否启用线程
   * @Author: peikunkun
   * @Date: 2020/3/2 0002 上午 10:02
   */
  @SuppressWarnings("all")
  public JSONArray exec(Set<String> execPingTai, String execSQL, boolean forceExec) {
    JSONArray result = new JSONArray();
    boolean execApp = asck(execPingTai, execSQL, forceExec, null);
    if (!execApp) {
      return result;
    }

    //成功失败结果
    Set<String> success = new CopyOnWriteArraySet<>();
    Set<String> fail = new CopyOnWriteArraySet<>();

    //平台执行
    for (String sshName : execPingTai) {
      JSONObject resultJson = new JSONObject();
      try {
        result.add(new JSONObject().fluentPut(sshName, handleExec(execSQL, forceExec, success, fail, sshName)));
      } catch (SSHActiveException e) {
        result.add(e.getErrorParam());
      } catch (Exception e) {
        result.add(new JSONObject().fluentPut(sshName, null));
      }
    }
    //打印执行详情
    printExecDetail(success, fail);
    return result;
  }


  /**
   * @Description: 多线程执行SQL
   * @Param: execPingTai: 执行平台集合
   * @Param execSQL: 执行 sql
   * @Param forceExec: 单个平台一个sql出错,是否继续执行下一个sql
   * @Param coreThreadNum: 核心线程数量
   * @return: com.alibaba.fastjson.JSONArray
   * @Author: peikunkun
   * @Date: 2020/3/5 0005 上午 10:21
   */
  @SuppressWarnings("all")
  private JSONArray execThread(Set<String> execPingTai, String execSQL, boolean forceExec,
      AbstractWebSocketServer webSocketServer) {
    JSONArray result = new JSONArray();
    boolean execApp = asck(execPingTai, execSQL, forceExec, webSocketServer);
    if (!execApp) {
      return result;
    }

    //成功失败结果
    Set<String> success = new CopyOnWriteArraySet<>();
    Set<String> fail = new CopyOnWriteArraySet<>();

    List<Future> futures = new ArrayList<>();

    //平台执行
    for (String sshName : execPingTai) {
      futures.add(executorService.submit(new Callable<JSONObject>() {
        @Override
        public JSONObject call() throws Exception {
          return new JSONObject().fluentPut(sshName, handleExec(execSQL, forceExec, success, fail, sshName));
        }
      }));
    }

    //shutdown()：停止接收新任务，原来的任务继续执行
    //executorService.shutdown();
    for (int i = 0; i < futures.size(); i++) {
      try {
        result.add((JSONObject) futures.get(i).get());
      } catch (SSHActiveException e0) {
        result.add(e0.getErrorParam());
      } catch (InterruptedException e1) {
        result.add(new JSONObject().fluentPut(e1.getMessage(), null));
      } catch (ExecutionException e) {
        if (e.getCause() instanceof SSHActiveException) {
          result.add(((SSHActiveException) e.getCause()).getErrorParam());
        } else {
          result.add(new JSONObject().fluentPut(e.getMessage(), null));
        }
      }
    }

    //打印执行详情
    final String log = printExecDetail(success, fail);
    if (null != webSocketServer) {
      WebScoketUtil.broadcostOperate(webSocketServer.getWebSocket(), log);
    }
    return result;
  }

  /**
   * @Description: 打印执行详情
   * @Param: success
   * @Param fail
   * @return: void
   * @Author: peikunkun
   * @Date: 2020/3/4 0004 下午 4:05
   */
  private static String printExecDetail(Set<String> success, Set<String> fail) {
    StringBuffer sb = new StringBuffer();
    sb.append("\n┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
    sb.append("┃\n");
    sb.append("┃\n");
    sb.append("┃ 执行成功:" + JSONObject.toJSONString(success) + "\n");
    sb.append("┃ 执行失败:" + JSONObject.toJSONString(fail) + "\n");
    sb.append("┃ 成功数量:" + success.size() + "\n");
    sb.append("┃ 失败数量:" + fail.size() + "\n");
    sb.append("┃\n");
    sb.append("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n\n");
    log.info(sb.toString());
    return sb.toString();
  }

  private JSONObject handleExec(String execSQL, boolean forceExec, Set<String> success, Set<String> fail,
      String sshName) throws SQLException, JSchException {
    JSONObject resultJson = null;
    try {
      resultJson = sshConnectionSupport.executeSSHDBSql(sshName, execSQL, forceExec);
      log.info("【{}】:[{}]", sshName, JSONObject.toJSONString(resultJson));
      success.add(sshName);
    } catch (SSHActiveException e) {
      //后面捕获异常时使用
      fail.add(sshName);
      throw e;
    } catch (Exception e) {
      fail.add(sshName);
      throw e;
    }
    return resultJson;
  }

  /**
   * @Description: 询问是否确认执行
   * @Param: execPingTai
   * @Param execSQL
   * @Param forceExec
   * @return: boolean
   * @Author: peikunkun
   * @Date: 2020/3/2 0002 下午 5:26
   */
  public boolean asck(Set<String> execPingTai, String execSQL, boolean forceExec,
      AbstractWebSocketServer webSocketServer) {
    StringBuffer sb = new StringBuffer();
    sb.append("\n┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
    sb.append("┃\n");
    sb.append("┃ 执行平台:" + JSONObject.toJSONString(execPingTai) + "\n");
    sb.append("┃ 平台数量:" + execPingTai.size() + "\n");
    sb.append("┃ 执行脚本:" + JSONObject.toJSONString(execSQL) + "\n");
    sb.append("┃ 迫切执行:" + JSONObject.toJSONString(forceExec) + "\n");
    if (coreThread > 0) {
      sb.append("┃ 启用线程:" + coreThread + "\n");
    }
    sb.append("┃ 请确认(yes/no):\n");
    sb.append("┃");
    String outLog =
        sb.toString() + "预计十秒之后开始执行\n┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n";
    if (null != webSocketServer) {
      WebScoketUtil.broadcostOperate(webSocketServer.getWebSocket(), outLog);
    }
    log.info(sb.toString());
    for (int i = 10; i >= 0; i--) {
      try {
        log.info("┃倒计时:[" + i + "s]");
        Thread.sleep(1000L);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    /*Scanner scan = new Scanner(System.in);
    // 判断是否还有输入
    String askStr = "";
    if (scan.hasNext()) {
      askStr = scan.next();
      log.info("┃[{}]", askStr);
    }
    scan.close();*/
    log.info("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛");
    /*if ("yes".equalsIgnoreCase(askStr)) {
      return true;
    }
    return false;*/

    return true;
  }
}
